package generic.net;

import generic.net.Response;
import generic.net.Request;
import generic.net.Peer;
import generic.net.PendingResponse;
import generic.net.client.Client;
import generic.net.observers.MessageObserver;
import generic.net.server.Server;
import generic.net.server.Service;
import org.junit.Assert.*;
import org.junit.*;

/**
 * Tests end-to-end client server communication.
 *
 * @author Adam Scarr <scarr.adam@gmail.com>
 */
public class ClientServerTest {
    @Test
    /**
     * Bi-directional communication test.
     *
     * The client sends a PING to the server, which should reply with a PONG.
     */
    public void test_ping() throws InterruptedException {
        System.out.println("::::PING TEST::::");
        Server server = new Server(1234);
        Client client = new Client("localhost", 1234);

        server.getServiceManager().add(new PingService());

        PendingResponse pr = client.sendRequest("PingProvider", "PING");
        Response response = pr.getResponse();

        Assert.assertEquals((String)response.getPayload(), "PONG");

        client.disconnect();
        server.stop();
    }


    @Test
    /**
     * Broadcast test.
     *
     * Make sure we can broadcast to multiple clients.
     */
    public void test_broadcast() {
        System.out.println("::::BROADCAST TEST::::");
        Server server = new Server(1235);
        Client client = new Client("localhost", 1235);
        Client broadcaster = new Client("localhost", 1235);

        BroadcastObserver bcListener = new BroadcastObserver();

        client.getMessageDispatcher().add(bcListener);

        server.getServiceManager().add(new BroadcastService());

        System.out.println("Waiting for response...");
        Response response = broadcaster.sendRequest("BroadcastProvider", "BROADCAST").getResponse();
        Assert.assertEquals((String)response.getPayload(), "OK");
        
        System.out.println("Got it... waiting for TO_ALL");
        Assert.assertEquals(bcListener.getMessage().getType(), "TO_ALL");

        client.disconnect();
        broadcaster.disconnect();
        server.stop();
    }

    private class BroadcastObserver implements MessageObserver {
        private Message message;
        private Thread sleeper;

        public void messageReceived(Peer peer, Message message) {
            if(message.getType().equals("TO_ALL")) {
                this.message = message;
                if(sleeper != null) {
                    sleeper.interrupt();
                }
            }
        }

        public synchronized Message getMessage() {
            sleeper = Thread.currentThread();
            while(message == null) {
                System.out.println("All in all it was all just messages in the wall: " + message);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException ex) {}
            }
            
            return message;
        }
    }

    /**
     * Broadcast server will send a message to all connected clients when it
     * receives a broadcast request. It will also respond to the request with OK
     */
    private class BroadcastService implements Service {
        public String getName() {
            return "BroadcastProvider";
        }

        public void messageReceived(Peer peer, Message message, Server server) {}

        public void requestReceived(Peer peer, Request request, Server server) {
            System.out.println("[SERVICE] Received request #" + request.getID());
            if(request.getRequestType().equals("BROADCAST")) {
                server.sendAll(new Message("TO_ALL"));
                peer.sendResponse(request, "OK");
            }
        }
    }

    /**
     * Replys to any ping with a pong...
     */
    private class PingService implements Service {
        public String getName() {
            return "PingProvider";
        }

        public void messageReceived(Peer peer, Message message, Server server) {}

        public void requestReceived(Peer peer, Request request, Server server) {
            if(request.getRequestType().equals("PING")) {
                peer.sendResponse(request, "PONG");
            }
        }
    }
}
